#include "mqtt.h"
#include "config.h"
#include <MQTTClient.h>  //MQTT客户端函数库
#include <cjson/cJSON.h> //cJSON头文件
#include <stdio.h>
#include <string.h> //strlen
#include <unistd.h> //sleep

//定义客户端变量
MQTTClient client;
//配置参数
struct config cfg;

//设置设备开关状态
//@param key 设备名称
//@param id  设备ID
//@param status 设备状态
//@retval 成功返回0，失败返回错误码
int set_status(char *key, int id, int status)
{
    MQTTClient_message msg = MQTTClient_message_initializer;
    int error = 0;
    MQTTClient_deliveryToken dt;

    //创建JSON对象
    cJSON *cmd = cJSON_CreateObject();
    if (!cmd)
    {
        return 1;
    }

    //在对象中添加布尔类型属性
    //参数1：指定对象的指针
    //参数2：属性名称
    //参数3：布尔值（1-真，0-假）
    cJSON_AddBoolToObject(cmd, key, status);

    //在对象中添加ID（数字）属性
    cJSON_AddNumberToObject(cmd, "id", id);

    //将JSON对象转换成字符串
    msg.payload = cJSON_PrintUnformatted(cmd);
    // printf("< %s\n", (char *)msg.payload);
    msg.payloadlen = strlen(msg.payload);
    msg.qos = 1;

    error = MQTTClient_publishMessage(client, cfg.topic_pub, &msg, &dt);
    if (error != MQTTCLIENT_SUCCESS)
    {
        printf("message publish error: %d\n", error);
        cJSON_free(msg.payload);
        cJSON_Delete(cmd);
        return error;
    }

    MQTTClient_waitForCompletion(client, dt, 1000);

    //释放分配的内存
    cJSON_free(msg.payload);
    cJSON_Delete(cmd);

    return 0;
}

//定义接收消息处理函数（回调）
//参数1：通过MQTTClient_setCallbacks传递的参数
//参数2：接收消息的主题名称
//参数3：接收消息的主题名称长度，当主题名称中有1个以上的0字符，需要使用此字段
//参数4：接收到的消息
//返回值：消息成功处理返回true（1），消息处理失败返回false（0）
int messageArrived(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    char *payload = message->payload;

#if 0
    printf("> ");
    // payloadlen 消息内容长度
    //消息内容放在 message->payload 字段指向的内存中
    for (int i = 0; i < message->payloadlen; i++)
    {
        putchar(payload[i]);
    }
    putchar('\n');
#endif

    // 由于MQTT消息内容不以0作为结束符，因此不能使用cJSON_Parse解析
    //参数1：需要解析的字符串首地址
    //参数2：需要解析的字符串长度
    cJSON *obj = cJSON_ParseWithLength(payload, message->payloadlen);
    if (obj) //解析成功
    {
        cJSON *flame = cJSON_GetObjectItem(obj, "flame");
        if (flame) //消息内容中包含火焰传感器状态
        {
            if (flame->valueint) //有火焰
            {
                //打开声光报警器
                set_status("alarm", 0, 1);
            }
            else
            { //没有火焰
                //关闭声光报警器
                set_status("alarm", 0, 0);
            }
        }

        cJSON_Delete(obj);
    }

    return 1;
}

int mqtt_init(void)
{
    int error = 0;
    //定义连接选项变量并初始化
    MQTTClient_connectOptions options = MQTTClient_connectOptions_initializer;

    //读取并解析配置文件
    if (parse_file("config.json", &cfg))
    {
        return 1;
    }

    // 1. 创建MQTT客户端
    //参数1：客户端变量的内存地址
    //参数2：MQTT服务器地址(协议://服务器地址或域名:端口号)
    //参数3：客户端ID
    //参数4：消息缓存方式（文件/内存）
    //参数5：用户自定义缓存方式的时候使用（默认填NULL）
    error = MQTTClient_create(&client, cfg.server_url, cfg.client_id, MQTTCLIENT_PERSISTENCE_NONE, NULL);
    if (error != MQTTCLIENT_SUCCESS)
    {
        printf("Create client error\n");
        return 1;
    }

    // 2.0 注册消息处理函数
    //此函数必须在连接服务器之前调用
    //参数1：客户端变量
    //参数2：传递给回调函数的参数，不需要传递参数时填NULL
    //参数3：当客户端与服务器断开连接的时候调用的回调函数，不需要处理的时候填NULL
    //参数4：当客户端收到消息后调用此函数进行处理，不需要处理的时候填NULL
    //参数5：消息发布成功后会调用此函数，不需要处理的时候填NULL
    error = MQTTClient_setCallbacks(client, NULL, NULL, messageArrived, NULL);
    if (error != MQTTCLIENT_SUCCESS)
    {
        printf("Set callbacks error\n");
    }

    // 2. 连接MQTT服务器
    error = MQTTClient_connect(client, &options);
    if (error != MQTTCLIENT_SUCCESS)
    {
        printf("Server connect error\n");
        return 1;
    }

    // 2.1 订阅主题
    //参数1：客户端变量
    //参数2：需要订阅的主题名称
    //参数3：服务质量级别（0-2）(恰好一次)
    error = MQTTClient_subscribe(client, cfg.topic_sub, 2);
    if (error != MQTTCLIENT_SUCCESS)
    {
        printf("Subscribe error\n");
    }

    return 0;
}

void mqtt_cleanup(void)
{
    // 6. 断开服务器连接
    //参数2：超时时间（单位毫秒）
    MQTTClient_disconnect(client, 1000);

    // 7. 释放客户端资源
    MQTTClient_destroy(&client);
}
